#include <cstdio>
#include <iostream>
#include <fstream>
#include <sstream>

#include <gdk/gdkevents.h>
#include <gdk/gdkkeysyms.h>
#include <gtkmm/main.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/widget.h>
#include <sigc++/sigc++.h>

#include "dbWindow.h"
#include "config.h"

#define BEAT_FREQ 880
#define BEAT_DUR 100000000 /* Nanoseconds */

using namespace std;
using namespace Gtk;

dbWindow::dbWindow () :
  _table (5, 5),
  _bpm_adjustment (120.0, 40.0, 240.0, 1.0, 10.0, 0),
  _bpm_spin_button (_bpm_adjustment),
  _bpm_label ("BPM:"),
  _beat_adjustment (4.0, 1.0, 100.0, 1.0, 10.0),
  _beat_spin_button (_beat_adjustment),
  _beat_label ("Beats:"),
  _accent_adjustment (0.0, 0.0, 12.0),
  _accent_scale (_accent_adjustment),
  _accent_label ("Accent"),
  _quarter_adjustment (0.0, -30.0, -12.0),
  _quarter_scale (_quarter_adjustment),
  _quarter_label ("1/4"),
  _eight_adjustment (0.0, -30.0, -12.0),
  _eight_scale (_eight_adjustment),
  _eight_label ("1/8"),
  _sixteen_adjustment (0.0, -30.0, -12.0),
  _sixteen_scale (_sixteen_adjustment),
  _sixteen_label ("1/16"),
  _triplet_adjustment (0.0, -30.0, -12.0),
  _triplet_scale (_triplet_adjustment),
  _triplet_label ("1/3"),
  _master_adjustment (0.0, -30.0, -12.0),
  _master_scale (_master_adjustment),
  _master_label ("Volume:"),
  _start_stop_button ("Start"),
  cb_params (string (MEDIADIR "/" TAP_SOUND))
{
  _error = false;

  set_title ("Dr. Beat");

  Main::signal_key_snooper ().connect (sigc::mem_fun (*this,
    &dbWindow::key_pressed));

  _table.attach (_bpm_label, 0, 1, 0, 1);
  _table.attach (_bpm_spin_button, 1, 2, 0, 1);
  _bpm_spin_button.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::bpm_spin_button_value_changed));


  _table.attach (_beat_label, 3, 4, 0, 1);
  _table.attach (_beat_spin_button, 4, 5, 0, 1);
  _beat_spin_button.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::beat_spin_button_value_changed));

  _accent_scale.set_draw_value (false);
  _accent_scale.set_inverted (true);
  _accent_scale.set_size_request (-1, 100);
  _accent_label.set_size_request (60, -1);
  _table.attach (_accent_scale, 0, 1, 2, 3);
  _table.attach (_accent_label, 0, 1, 1, 2);
  _accent_scale.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::accent_scale_value_changed));

  _quarter_scale.set_draw_value (false);
  _quarter_scale.set_inverted (true);
  _quarter_label.set_size_request (60, -1);
  _table.attach (_quarter_scale, 1, 2, 2, 3);
  _table.attach (_quarter_label, 1, 2, 1, 2);
  _quarter_scale.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::quarter_scale_value_changed));

  _eight_scale.set_draw_value (false);
  _eight_scale.set_inverted (true);
  _eight_label.set_size_request (60, -1);
  _table.attach (_eight_scale, 2, 3, 2, 3);
  _table.attach (_eight_label, 2, 3, 1, 2);
  _eight_scale.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::eight_scale_value_changed));

  _sixteen_scale.set_draw_value (false);
  _sixteen_scale.set_inverted (true);
  _sixteen_label.set_size_request (60, -1);
  _table.attach (_sixteen_scale, 3, 4, 2, 3);
  _table.attach (_sixteen_label, 3, 4, 1, 2);
  _sixteen_scale.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::sixteen_scale_value_changed));

  _triplet_scale.set_draw_value (false);
  _triplet_scale.set_inverted (true);
  _triplet_label.set_size_request (60, -1);
  _table.attach (_triplet_scale, 4, 5, 2, 3);
  _table.attach (_triplet_label, 4, 5, 1, 2);
  _triplet_scale.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::triplet_scale_value_changed));

  _master_scale.set_draw_value (false);
  _table.attach (_master_scale, 1, 4, 3, 4);
  _table.attach (_master_label, 0, 1, 3, 4);
  _master_scale.signal_value_changed ().connect (sigc::mem_fun (*this,
    &dbWindow::master_scale_value_changed));

  _start_stop_button.set_size_request (-1, 40);
  _table.attach (_start_stop_button, 0, 5, 4, 5);
  _start_stop_button.signal_toggled ().connect (sigc::mem_fun (*this,
    &dbWindow::start_stop_button_toggled));

  add (_table);
  show_all_children ();

  SDL_AudioSpec reqSpec;
  reqSpec.freq = 48000;
  reqSpec.format = AUDIO_U8;
  reqSpec.channels = 1;
  reqSpec.samples = 1024;
  reqSpec.callback = audio_callback;
  reqSpec.userdata = &cb_params;

  stringstream file_path;
  file_path << getenv ("HOME");
  file_path << "/.drbeat";
  cb_params.read_settings (file_path.str ());

  _bpm_spin_button.set_value ((double)cb_params.get_bpm ());
  _beat_spin_button.set_value ((double)cb_params.get_beats ());
  _accent_scale.set_value ((double)cb_params.get_volume (Accent));
  _quarter_scale.set_value ((double)cb_params.get_volume (Quarter));
  _eight_scale.set_value ((double)cb_params.get_volume (Eight));
  _sixteen_scale.set_value ((double)cb_params.get_volume (Sixteen));
  _triplet_scale.set_value ((double)cb_params.get_volume (Triplet));
  _master_scale.set_value ((double)cb_params.get_volume (Master));

  SDL_AudioSpec s;
  if (SDL_OpenAudio (&reqSpec, &s) == -1) {
    MessageDialog dlg ("Unable to initialize audio device", false,
      MESSAGE_ERROR, BUTTONS_OK, false);
    dlg.run ();
    _error = true;
  }
  cb_params.set_spec (s);

  if (!(cb_params.get_audio_wav ())) {
    MessageDialog dlg ("Unable to load " TAP_SOUND, false,
      MESSAGE_ERROR, BUTTONS_OK, false);
    dlg.run ();
    _error = true;
  }
}

dbWindow::~dbWindow ()
{
  stringstream file_path;
  file_path << getenv ("HOME");
  file_path << "/.drbeat";
  cb_params.write_settings (file_path.str ());
}

bool dbWindow::operator! () const
{
  return _error;
}

int
dbWindow::key_pressed (Widget *w, GdkEventKey *evt)
{
  if (evt->type == GDK_KEY_RELEASE)
    return 0;
  
  if (evt->keyval == GDK_a)
    _accent_scale.set_value (_accent_scale.get_value () + 0.6);
  else if (evt->keyval == GDK_z)
    _accent_scale.set_value (_accent_scale.get_value () - 0.6);
  else if (evt->keyval == GDK_s)
    _quarter_scale.set_value (_quarter_scale.get_value () + 1.9);
  else if (evt->keyval == GDK_x)
    _quarter_scale.set_value (_quarter_scale.get_value () - 1.9);
  else if (evt->keyval == GDK_d)
    _eight_scale.set_value (_eight_scale.get_value () + 1.9);
  else if (evt->keyval == GDK_c)
    _eight_scale.set_value (_eight_scale.get_value () - 1.9);
  else if (evt->keyval == GDK_f)
    _sixteen_scale.set_value (_sixteen_scale.get_value () + 1.9);
  else if (evt->keyval == GDK_v)
    _sixteen_scale.set_value (_sixteen_scale.get_value () - 1.9);
  else if (evt->keyval == GDK_g)
    _triplet_scale.set_value (_triplet_scale.get_value () + 1.9);
  else if (evt->keyval == GDK_b)
    _triplet_scale.set_value (_triplet_scale.get_value () - 1.9);
  else if (evt->keyval == GDK_space)
    _start_stop_button.set_active (!_start_stop_button.get_active ());
  else if (evt->keyval == GDK_comma)
    _bpm_spin_button.set_value (_bpm_spin_button.get_value () - 5);
  else if (evt->keyval == GDK_period)
    _bpm_spin_button.set_value (_bpm_spin_button.get_value () + 5);
  else if (evt->keyval == GDK_bracketleft)
    _beat_spin_button.set_value (_beat_spin_button.get_value () - 1);
  else if (evt->keyval == GDK_bracketright)
    _beat_spin_button.set_value (_beat_spin_button.get_value () + 1);
  else
    return 0;

  return 1;
}

void dbWindow::bpm_spin_button_value_changed ()
{
  cb_params.set_bpm ((int)_bpm_spin_button.get_value ());
}

void dbWindow::beat_spin_button_value_changed ()
{
  cb_params.set_beats ((int)_beat_spin_button.get_value ());
}

void dbWindow::start_stop_button_toggled ()
{
  if (_start_stop_button.get_active ()) {
    SDL_PauseAudio (0); /* Start audio */
    _start_stop_button.set_label ("Stop");
  }
  else {
    SDL_PauseAudio (1); /* Stop audio */
    _start_stop_button.set_label ("Start");
  }
}

void dbWindow::accent_scale_value_changed ()
{
  cb_params.set_volume (Accent, (int)_accent_scale.get_value ());
}

void dbWindow::quarter_scale_value_changed ()
{
  cb_params.set_volume (Quarter, (int)_quarter_scale.get_value ());
}

void dbWindow::eight_scale_value_changed ()
{
  cb_params.set_volume (Eight, (int)_eight_scale.get_value ());
}

void dbWindow::sixteen_scale_value_changed ()
{
  cb_params.set_volume (Sixteen, (int)_sixteen_scale.get_value ());
}

void dbWindow::triplet_scale_value_changed ()
{
  cb_params.set_volume (Triplet, (int)_triplet_scale.get_value ());
}

void dbWindow::master_scale_value_changed ()
{
  cb_params.set_volume (Master, (int)_master_scale.get_value ());
}
